/*
 * Copyright (c) 2020 pig4cloud Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.xunk.gateway.filter;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.Mode;
import cn.hutool.crypto.Padding;
import cn.hutool.crypto.symmetric.AES;
import cn.hutool.http.HttpUtil;
import com.xunk.common.core.constant.Constants;
import com.xunk.common.core.constant.SecurityConstants;
import com.xunk.common.redis.service.RedisService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.util.UriComponentsBuilder;

import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.Map;

/**
 * AES加解密
 */
@Slf4j
@Component
public class PasswordDecoderFilter extends AbstractGatewayFilterFactory<Object> {

    private static final String PASSWORD = "password";
    private static final String GRANT_TYPE = "grant_type";
    private static final String AUTH_CODE_TYPE = "auth_code";

    private static final String KEY_ALGORITHM = "AES";

    private static final String KEY = "_aes_secret_key_";
    private static final String IV = "_aes_secret_iv__";

    @Autowired
    RedisService redisService;

    @Override
    public GatewayFilter apply(Object config) {

        return (exchange, chain) -> {
            ServerHttpRequest request = exchange.getRequest();
            // 不是登录请求，直接向下执行
            if (!StrUtil.containsAnyIgnoreCase(request.getURI().getPath(), SecurityConstants.AUTH_TOKEN)) {
                return chain.filter(exchange);
            }

            URI uri = exchange.getRequest().getURI();
            String queryParam = uri.getRawQuery();
            Map<String, String> paramMap = HttpUtil.decodeParamMap(queryParam, CharsetUtil.CHARSET_UTF_8);

            String grantType = paramMap.get(GRANT_TYPE);
            if (AUTH_CODE_TYPE.equalsIgnoreCase(grantType)) {
                String phone = paramMap.get("username");
                String pKey = Constants.USER_LOGIN_PASSWORD + phone;
                String value = redisService.getCacheObject(pKey);
                paramMap.put(GRANT_TYPE, PASSWORD);
                paramMap.put(PASSWORD, value.split(":")[0]);
            } else {
                String password = paramMap.get(PASSWORD);
                log.info("解密密码");
                if (StrUtil.isNotBlank(password)) {
                    try {
                        password = decryptAES(password, KEY, IV);
                    } catch (Exception e) {
                        log.error("密码解密失败:{}", password);
                        // 解密失败就按照密码错误，清空密码
                        password = "";
                    }
                    paramMap.put(PASSWORD, password.trim());
                }
            }

            URI newUri = UriComponentsBuilder.fromUri(uri).replaceQuery(HttpUtil.toParams(paramMap)).build(true)
                    .toUri();

            ServerHttpRequest newRequest = exchange.getRequest().mutate().uri(newUri).build();
            return chain.filter(exchange.mutate().request(newRequest).build());
        };
    }


    private static String decryptAES(String data, String key, String iv) {
        AES aes = new AES(Mode.CBC, Padding.ZeroPadding, new SecretKeySpec(key.getBytes(), KEY_ALGORITHM), new IvParameterSpec(iv.getBytes()));
        byte[] result = aes.decrypt(Base64.decode(data.getBytes(StandardCharsets.UTF_8)));
        return new String(result, StandardCharsets.UTF_8);
    }

}
